﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;

namespace UDS.DB.Lib
{
    internal partial class ObjectDependencies
    {
        private const string BaseNS = "UDS.DB.";
        private static XmlSchemaSet s_xss;

        internal static ObjectDependencies LoadTables()
        {
            return LoadObjects(BaseNS + "tables.xml");
        }

        internal static ObjectDependencies LoadViews()
        {
            return LoadObjects(BaseNS + "views.xml");
        }

        private static ObjectDependencies LoadObjects(string embeddedResourceName)
        {
            var xss = GetXSDSchemaSet();
            var settings = new XmlReaderSettings();
            settings.Schemas = xss;
            settings.ValidationType = ValidationType.Schema;

            // Load the xml document
            var doc = new XmlDocument();
            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(embeddedResourceName))
            {
                if (stream == null)
                {
                    throw new InvalidOperationException(string.Format("The embeded resource '{0}' is missing!", embeddedResourceName));
                }
                using (var reader = XmlReader.Create(stream, settings))
                {
                    doc.Load(reader);
                }
            }

            // Parse the document
            var dependencies = new ObjectDependencies();
            var np = new XmlNamespaceManager(doc.NameTable);
            np.AddNamespace("v", "http://www.microsoft.com/chzhou/sql_object_dependencies.xsd");

            var nodes = doc.SelectNodes("/v:objects/v:obj", np);
            foreach (XmlElement node in nodes)
            {
                string objName = node.Attributes["name"].Value;
                var refNames = new List<string>();

                var childNodes = node.SelectNodes("./v:ref", np);
                foreach (XmlElement child in childNodes)
                {
                    string name = child.Attributes["name"].Value;
                    refNames.Add(name);
                }

                dependencies.AddDependencies(objName, refNames.AsReadOnly());
            }

            return dependencies;
        }

        private static XmlSchemaSet GetXSDSchemaSet()
        {
            if (s_xss == null)
            {
                // Load the xsd schema
                var xss = new XmlSchemaSet();
                const string XsdResourceName = BaseNS + "sql_object_dependencies.xsd";
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(XsdResourceName))
                {
                    if (stream == null)
                    {
                        throw new InvalidOperationException("The embeded resource '" + XsdResourceName + "' cannot be found.");
                    }
                    using (var reader = XmlReader.Create(stream))
                    {
                        xss.Add(null, reader);
                    }
                }
                if (xss.Count == 0)
                {
                    throw new InvalidOperationException("XSD schema have not been properly loaded!");
                }

                s_xss = xss;
            }
            return s_xss;
        }
    }
}